package main

import (
	"io"
	"net"
	"net/http"
	"net/url"
	"strconv"
	"time"

	"github.com/rs/cors"
)

type proxyHandler struct {
	client *http.Client
}

func (h *proxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	url, _ := url.Parse(r.Header.Get("X-Base-Url-Pdpfyuorps"))
	url.RawQuery = r.URL.RawQuery
	url.Path = r.URL.Path

	nr, err := http.NewRequestWithContext(r.Context(), r.Method, url.String(), r.Body)
	if err != nil {
		w.WriteHeader(500)
		return
	}
	nr.Header = r.Header.Clone()

	nr.Header.Del("Connection")
	nr.Header.Del("X-Base-Url-Pdpfyuorps")

	cookie := nr.Header.Get("X-Cookie-Pdpfyuorps")
	nr.Header.Del("X-Cookie-Pdpfyuorps")
	nr.Header.Set("Cookie", cookie)

	// TODO:
	nr.Header.Del("Origin")
	nr.Header.Del("Referer")

	resp, err := h.client.Do(nr)
	if err != nil {
		w.WriteHeader(502)
		return
	}
	for name, values := range resp.Header {
		for _, value := range values {
			if name == "Set-Cookie" {
				name = "X-Set-Cookie-Pdpfyuorps"
			}
			if name != "Access-Control-Allow-Credentials" &&
				name != "Access-Control-Allow-Headers" &&
				name != "Access-Control-Allow-Method" &&
				name != "Access-Control-Allow-Origin" &&
				name != "Access-Control-Expose-Headers" &&
				name != "Access-Control-Max-Age" {
				w.Header().Add(name, value)
			}
		}
	}
	w.Header().Add("Vary", "X-Base-Url-Pdpfyuorps")
	w.Header().Set("X-Status-Code-Pdpfyuorps", strconv.Itoa(resp.StatusCode))
	sc := 200
	if resp.StatusCode >= 400 {
		sc = 500
	}
	w.WriteHeader(sc)
	io.Copy(w, resp.Body)
	resp.Body.Close()
}

func main() {
	c := cors.New(cors.Options{
		ExposedHeaders:     []string{"X-Status-Code-Pdpfyuorps", "X-Set-Cookie-Pdpfyuorps", "Location"},
		AllowCredentials:   true,
		AllowedMethods:     []string{"GET", "HEAD", "PUT", "PATCH", "POST", "DELETE"},
		AllowedHeaders:     []string{"*"},
		OptionsPassthrough: false,
		MaxAge:             86400,
	})
	h := c.Handler(&proxyHandler{client: &http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			DialContext: (&net.Dialer{
				Timeout:   30 * time.Second,
				KeepAlive: 30 * time.Second,
				DualStack: true,
			}).DialContext,
			ForceAttemptHTTP2:     true,
			MaxIdleConns:          100,
			MaxIdleConnsPerHost:   20,
			MaxConnsPerHost:       0,
			IdleConnTimeout:       90 * time.Second,
			TLSHandshakeTimeout:   10 * time.Second,
			ExpectContinueTimeout: 1 * time.Second,
		},
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return http.ErrUseLastResponse
		},
	}})
	http.ListenAndServe(":3000", h)
}
